#pragma once

#include "Common.hpp"
#include "Log.hpp"

#include <unistd.h>
#include <sys/epoll.h>

using namespace logModule;

class Epoller
{
    static const int defaulfd = -1;

private:

    bool _set_epoll_ctl(int oper, int sockfd, struct epoll_event* ev, const std::string& info)
    {
        int n = epoll_ctl(_epfd, oper, sockfd, ev);
        if (n < 0)
        {
            LOG(LogLevel::WARNING) << "epoll_ctl " << info << " sockfd:" << sockfd << " failure";
            return false;
        }
        LOG(LogLevel::DEBUG) << "epoll_ctl " << info << " sockfd:" << sockfd << " success";
        return true;
    }

    struct epoll_event create_epoll_event(int fd, uint32_t events)
    {
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = events;
        return ev; 
    }

public:

    Epoller()
        :_epfd(defaulfd)
    {
        _epfd = epoll_create(256);
        if (_epfd < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_create failure";
            exit(EPOLL_CREATE_ERR);
        }
        LOG(LogLevel::DEBUG) << "epoll_create success";
    }

    bool addEvent(int sockfd, uint32_t events)
    {
        struct epoll_event ev = create_epoll_event(sockfd, events);
        return _set_epoll_ctl(EPOLL_CTL_ADD, sockfd, &ev, "add");
    }

    bool delEvent(int sockfd)
    {
        return _set_epoll_ctl(EPOLL_CTL_DEL, sockfd, nullptr, "remove");
    }

    bool setEvent(int sockfd, uint32_t events)
    {
        struct epoll_event ev = create_epoll_event(sockfd, events);
        return _set_epoll_ctl(EPOLL_CTL_MOD, sockfd, &ev, "set");
    }

    int waitEvent(struct epoll_event revs[], int maxnum, int timeout)
    {
        int n = epoll_wait(_epfd, revs, maxnum, timeout);
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_wait error";
        }
        else if (n == 0)
        {
            LOG(LogLevel::INFO) << "time out...";
        }
        else
        {
            LOG(LogLevel::DEBUG) << "就绪事件数量:" << n;
        }

        return n;
    }

    ~Epoller()
    {
        if (_epfd > 0)
        {
            ::close(_epfd);
        }
    }
    
private:

    int _epfd;
};

